import requests
import hashlib
import random
import time
import re
import json
import os
from PIL import Image
from io import BytesIO
from urllib.parse import unquote, urlparse, urlencode


class FnosMediaFetcher:
    # 配置参数
    API_PATH_PREFIX = "/v/api/v1"
    LOGIN_API_ENDPOINT = "/login"
    MEDIA_LIST_API_ENDPOINT = "/item/list"
    IMAGE_API_ENDPOINT = "/sys/img"  # 图片基础路径
    MEDIADB_LIST_ENDPOINT = "/mediadb/list"
    FIXED_API_KEY = "16CCEB3D-AB42-077D-36A1-F355324E4237"
    SECRET_KEY = "NDzZTVxnRKP8Z0jXg1VAMonaG8akvh"
    APP_NAME = "trimemedia-web"
    PAGE_SIZE = 22
    DEBUG = True

    def __init__(self, server_ip, server_port, verify_ssl=False):
        self.server_ip = server_ip
        self.server_port = server_port
        self.token = None
        self.ancestor_guid = None
        self.session = requests.Session()
        self.session.verify = verify_ssl
        self.media_libraries = []
        if not verify_ssl:
            requests.packages.urllib3.disable_warnings()

        # 构建API URL
        self.LOGIN_API_URL = self._build_api_url(self.LOGIN_API_ENDPOINT)
        self.MEDIA_LIST_URL = self._build_api_url(self.MEDIA_LIST_API_ENDPOINT)
        self.IMAGE_API_URL = self._build_api_url(self.IMAGE_API_ENDPOINT)
        self.MEDIADB_LIST_URL = self._build_api_url(self.MEDIADB_LIST_ENDPOINT)

        if self.DEBUG:
            print(f"初始化API URL:")
            print(f"登录URL: {self.LOGIN_API_URL}")
            print(f"媒体库列表URL: {self.MEDIADB_LIST_URL}")
            print(f"媒体内容URL: {self.MEDIA_LIST_URL}")

    def _build_api_url(self, endpoint):
        return f"http://{self.server_ip}:{self.server_port}{self.API_PATH_PREFIX}{endpoint}"

    def _parse_url(self, url):
        return urlparse(url).path

    def _hash_string(self, input_str):
        return hashlib.md5(input_str.encode('utf-8')).hexdigest()

    def _process_for_hash(self, input_str):
        try:
            normalized = re.sub(r'%(?![0-9A-Fa-f]{2})', '%25', input_str)
            decoded = unquote(normalized, encoding='utf-8', errors='strict')
            return self._hash_string(decoded)
        except Exception as e:
            if self.DEBUG:
                print(f"哈希处理出错: {str(e)}")
            return self._hash_string(input_str)

    def _generate_authx(self, url, param_str):
        try:
            path = self._parse_url(url)
            param_hash = self._process_for_hash(param_str)
            nonce = str(random.randint(100000, 999999)).zfill(6)
            timestamp = str(int(time.time() * 1000))

            # 修改签名生成公式，确保与API服务端一致
            sign_str = f"{self.SECRET_KEY}_{path}_{nonce}_{timestamp}_{param_hash}_{self.FIXED_API_KEY}"
            
            if self.DEBUG:
                print(f"签名生成路径: {path}")
                print(f"参数哈希: {param_hash}")
                print(f"随机数: {nonce}")
                print(f"时间戳: {timestamp}")
                print(f"签名字符串: {sign_str}")
                
            sign = self._hash_string(sign_str)
            return f"nonce={nonce}&timestamp={timestamp}&sign={sign}"
        except Exception as e:
            if self.DEBUG:
                print(f"生成签名出错: {str(e)}")
                print(f"URL: {url}")
                print(f"参数字符串: {param_str}")
            raise

    def login(self, username, password):
        login_data = {
            "app_name": self.APP_NAME,
            "username": username,
            "password": password
        }
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0",
            "Content-Type": "application/json",
            "Referer": f"http://{self.server_ip}:{self.server_port}/v"
        }

        try:
            if self.DEBUG:
                print(f"发送登录请求到: {self.LOGIN_API_URL}")
                print(f"登录参数: {login_data}")

            resp = self.session.post(
                self.LOGIN_API_URL,
                headers=headers,
                json=login_data,
                timeout=15
            )

            if self.DEBUG:
                print(f"登录响应状态码: {resp.status_code}")
                print(f"登录响应内容: {resp.text}")

            result = resp.json()

            if result.get("code") == 0 and "token" in result.get("data", {}):
                self.token = result["data"]["token"]
                load_success, load_msg = self.load_media_libraries()
                return (True, f"登录成功。{load_msg}")
            return (False, result.get("msg", "未知错误"))
        except Exception as e:
            return (False, f"登录过程出错: {str(e)}")

    def stitch_library_posters(self, library_guid, output_path=None):
        """将指定媒体库的封面图片横向拼接为一张图片，命名为媒体名称_poster.png"""
        # 查找指定的媒体库
        library = None
        for lib in self.media_libraries:
            if lib["guid"] == library_guid:
                library = lib
                break

        if not library:
            return (False, f"未找到GUID为 {library_guid} 的媒体库")

        if not library["posters"]:
            return (False, f"媒体库 {library['title']} 没有封面图片")

        # 获取所有封面图片
        images = []
        for i, poster_url in enumerate(library["posters"], 1):
            print(f"正在获取 {library['title']} 的第 {i} 张封面...")
            success, img_data = self.fetch_image(poster_url)
            if success:
                # 将图片数据转换为PIL Image对象
                img = Image.open(BytesIO(img_data))
                images.append(img)
            else:
                print(f"获取第 {i} 张封面失败: {img_data}")

        if not images:
            return (False, "没有成功获取到任何封面图片")

        # 统一图片高度（按最小高度缩放）
        min_height = min(img.height for img in images)
        resized_images = []
        for img in images:
            # 按比例缩放图片以匹配最小高度
            ratio = min_height / img.height
            new_width = int(img.width * ratio)
            resized_img = img.resize((new_width, min_height), Image.LANCZOS)
            resized_images.append(resized_img)

        # 计算拼接后图片的总宽度和高度
        total_width = sum(img.width for img in resized_images)
        max_height = max(img.height for img in resized_images)

        # 创建新图片用于拼接
        stitched_img = Image.new('RGB', (total_width, max_height))

        # 拼接图片
        current_x = 0
        for img in resized_images:
            stitched_img.paste(img, (current_x, 0))
            current_x += img.width

        # 保存拼接后的图片，命名为媒体名称_poster.png
        if not output_path:
            # 自动生成文件名
            output_path = f"{library['title']}_poster.png"

        try:
            stitched_img.save(output_path)
            return (True, f"封面图片已成功拼接并保存至: {os.path.abspath(output_path)}")
        except Exception as e:
            return (False, f"保存拼接图片失败: {str(e)}")

    def save_media_info(self, media_items):
        """仅提取媒体条目的关键信息并返回，不生成JSON文件"""
        if not media_items:
            return (False, "没有可处理的媒体条目数据")

        try:
            # 提取需要的字段
            processed_info = []
            for item in media_items:
                # 查找所属媒体库名称
                library = None
                for lib in self.media_libraries:
                    if lib["guid"] == item.get("ancestor_guid"):
                        library = lib
                        break

                library_name = library["title"] if library else "未知媒体库"

                # 提取关键信息
                item_info = {
                    "唯一标识": item.get("guid", ""),
                    "标题": item.get("title", "未知标题"),
                    "类型": item.get("type", "未知类型"),
                    "语言": item.get("lan", "未知语言"),
                    "剧情简介": item.get("overview", "无简介"),
                    "时长(分钟)": item.get("runtime", "未知"),
                    "发行日期": item.get("release_date", "未知日期"),
                    "评分": item.get("vote_average", "无评分"),
                    "海报图片路径": item.get("poster", ""),
                    "海报完整URL": self.get_image_url(item.get("poster", "")),
                    "所属媒体库ID": item.get("ancestor_guid", ""),
                    "所属媒体库名称": library_name
                }
                processed_info.append(item_info)

            return (True, processed_info)
        except Exception as e:
            return (False, f"提取媒体信息失败: {str(e)}")

    def fetch_media(self, ancestor_guid=None, media_types=None):
        if not self.token:
            return (False, "未登录，请先调用login方法")

        target_guid = ancestor_guid or self.ancestor_guid
        if not target_guid:
            return (False, "未指定媒体库ID，请提供ancestor_guid参数")

        types = media_types or ["Movie", "TV", "Directory", "Video"]

        all_media = []
        current_page = 1
        total_count = None

        while True:
            params = {
                "ancestor_guid": target_guid,
                "tags": {"type": types},
                "exclude_grouped_video": 1,
                "sort_type": "DESC",
                "sort_column": "create_time",
                "page_size": self.PAGE_SIZE,
                "page": current_page
            }
            param_str = json.dumps(params, ensure_ascii=False, sort_keys=True)

            try:
                authx = self._generate_authx(self.MEDIA_LIST_URL, param_str)
                headers = {
                    "Authorization": self.token,
                    "Authx": authx,
                    "Content-Type": "application/json",
                    "Host": f"{self.server_ip}:{self.server_port}",
                    "Origin": f"http://{self.server_ip}:{self.server_port}",
                    "Referer": f"http://{self.server_ip}:{self.server_port}/v",
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
                }

                if self.DEBUG:
                    print(f"获取媒体列表，页码: {current_page}，媒体库ID: {target_guid}")
                    print(f"请求URL: {self.MEDIA_LIST_URL}")

                resp = self.session.post(
                    self.MEDIA_LIST_URL,
                    headers=headers,
                    data=param_str.encode('utf-8'),
                    timeout=20
                )

                if self.DEBUG:
                    print(f"媒体列表响应状态码: {resp.status_code}")
                    print(f"媒体列表响应内容: {resp.text}")

                result = resp.json()

                if result.get("code") != 0:
                    return (False, f"请求失败: {result.get('msg')}")

                data = result["data"]
                if not total_count:
                    total_count = data.get("total", 0)

                media_list = data.get("list", [])
                processed_media = self._process_media_poster(media_list)
                all_media.extend(processed_media)

                if len(all_media) >= total_count or not media_list:
                    break

                current_page += 1
                time.sleep(1)

            except Exception as e:
                return (False, f"获取媒体出错: {str(e)}")

        return (True, all_media)

    def fetch_mediadb_list(self):
        if not self.token:
            return (False, "未登录，请先调用login方法")

        params = {}
        param_str = urlencode(params, encoding='utf-8')

        try:
            authx = self._generate_authx(self.MEDIADB_LIST_URL, param_str)
            headers = {
                "Authorization": self.token,
                "Authx": authx,
                "Content-Type": "application/json",
                "Host": f"{self.server_ip}:{self.server_port}",
                "Origin": f"http://{self.server_ip}:{self.server_port}",
                "Referer": f"http://{self.server_ip}:{self.server_port}/v",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
            }

            if self.DEBUG:
                print(f"获取媒体库列表URL: {self.MEDIADB_LIST_URL}")
                print(f"GET参数: {param_str}")
                print(f"媒体库请求头: {headers}")

            resp = self.session.get(
                self.MEDIADB_LIST_URL,
                headers=headers,
                params=params,
                timeout=20
            )

            if self.DEBUG:
                print(f"媒体库列表响应状态码: {resp.status_code}")
                print(f"媒体库列表响应内容: {resp.text}")

            if resp.status_code != 200:
                return (False, f"HTTP状态码错误: {resp.status_code}")

            try:
                result = resp.json()
            except json.JSONDecodeError:
                return (False, f"响应不是有效的JSON格式: {resp.text[:100]}")

            # 检查API返回代码
            if "code" in result and result.get("code") != 0:
                return (False, f"API返回错误: {result.get('msg', '未知错误')}")

            # 获取数据并确保返回格式一致
            data = result.get("data", [])
            
            # 确保返回的数据是数组格式
            if not isinstance(data, list):
                # 如果数据不是数组，尝试转换为数组或返回空数组
                if self.DEBUG:
                    print(f"数据格式不符合预期，尝试转换: {type(data)}")
                data = [data] if data else []
            
            return (True, data)

        except Exception as e:
            return (False, f"获取媒体库列表出错: {str(e)}")

    def load_media_libraries(self):
        try:
            success, data = self.fetch_mediadb_list()
            if not success:
                return (False, f"加载媒体库列表失败: {data}")

            if not isinstance(data, list):
                return (False, f"媒体库数据格式错误，预期列表但得到: {type(data)}")

            processed_libraries = []
            for lib in data:
                processed_lib = {
                    "guid": lib.get("guid", ""),
                    "title": lib.get("title", "未知媒体库"),
                    "posters": [self.get_image_url(poster) for poster in lib.get("posters", [])],
                    "category": lib.get("category", "Unknown"),
                    "view_type": lib.get("view_type", 0)
                }
                processed_libraries.append(processed_lib)

            self.media_libraries = processed_libraries

            if processed_libraries:
                self.ancestor_guid = processed_libraries[0]["guid"]
                return (True, f"成功加载 {len(processed_libraries)} 个媒体库")
            else:
                return (True, "媒体库列表为空")

        except Exception as e:
            return (False, f"处理媒体库列表出错: {str(e)}")

    def get_media_libraries(self):
        return self.media_libraries

    def _process_media_poster(self, media_list):
        processed = []
        for media in media_list:
            if "poster" in media and media["poster"]:
                media["poster"] = self.get_image_url(media["poster"])
                # 检查封面URL有效性
                media["poster_valid"] = self._check_url_validity(media["poster"])
            else:
                media["poster_valid"] = False
            processed.append(media)
        return processed

    def get_image_url(self, image_path):
        """生成正确的图片URL，使用路径拼接而非参数形式"""
        if image_path.startswith(("http://", "https://")):
            return image_path

        # 移除image_path开头的斜杠（如果有），避免出现双斜杠
        cleaned_path = image_path.lstrip('/')
        # 直接拼接路径
        return f"{self.IMAGE_API_URL}/{cleaned_path}"

    def _check_url_validity(self, url):
        """检查URL是否可访问（仅发送HEAD请求）"""
        try:
            headers = {
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0",
                "Authorization": self.token,
                "Authx": self._generate_authx(url, "")  # 为URL检查生成签名
            }
            resp = self.session.head(url, headers=headers, timeout=5, allow_redirects=True)
            return resp.status_code in [200, 301, 302]
        except:
            return False

    def fetch_image(self, image_path):
        if not self.token:
            return (False, "未登录，请先调用login方法")

        image_url = self.get_image_url(image_path)
        try:
            # 为图片请求生成签名
            authx = self._generate_authx(image_url, "")
            headers = {
                "Authorization": self.token,
                "Authx": authx,
                "Host": f"{self.server_ip}:{self.server_port}",
                "Origin": f"http://{self.server_ip}:{self.server_port}",
                "Referer": f"http://{self.server_ip}:{self.server_port}/v",
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"
            }

            resp = self.session.get(
                image_url,
                headers=headers,
                timeout=20
            )

            if resp.status_code == 200:
                return (True, resp.content)
            return (False, f"图片获取失败，状态码: {resp.status_code}")

        except Exception as e:
            return (False, str(e))

    def set_ancestor_guid(self, guid):
        self.ancestor_guid = guid

    def set_server_info(self, server_ip, server_port):
        self.server_ip = server_ip
        self.server_port = server_port
        self.LOGIN_API_URL = self._build_api_url(self.LOGIN_API_ENDPOINT)
        self.MEDIA_LIST_URL = self._build_api_url(self.MEDIA_LIST_API_ENDPOINT)
        self.IMAGE_API_URL = self._build_api_url(self.IMAGE_API_ENDPOINT)
        self.MEDIADB_LIST_URL = self._build_api_url(self.MEDIADB_LIST_ENDPOINT)
